package la.iok.hzsvn.lewin.movie.operation.service;

import la.iok.hzsvn.lewin.movie.core.RoleEnum;
import la.iok.hzsvn.lewin.movie.core.Token;
import la.iok.hzsvn.lewin.movie.core.exception.ErrorCode;
import la.iok.hzsvn.lewin.movie.core.model.PlayRoleInstVo;
import la.iok.hzsvn.lewin.movie.core.model.QR;
import la.iok.hzsvn.lewin.movie.core.model.UuidParam;
import la.iok.hzsvn.lewin.movie.operation.entity.MoviePlayer;
import la.iok.hzsvn.lewin.movie.operation.entity.PlayRecord;
import la.iok.hzsvn.lewin.movie.operation.mapper.MoviePlayerMapper;
import la.iok.hzsvn.lewin.movie.operation.model.MoviePlayerVo;
import la.iok.hzsvn.lewin.movie.services.MovieServiceInvoker;
import la.iok.hzsvn.lewin.mybatis.service.BaseServiceImpl;
import la.iok.hzsvn.redis.config.RedisKeyProperties;
import la.iok.hzsvn.redis.service.RedisService;
import la.iok.hzsvn.share.annotations.NotNull;
import la.iok.hzsvn.share.annotations.NotNullElement;
import la.iok.hzsvn.share.annotations.Nullable;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Service
public class MoviePlayerServiceImpl extends BaseServiceImpl<MoviePlayer, MoviePlayerMapper, Long> implements MoviePlayerService {
    private final RedisService redisService;
    private final RedisKeyProperties redisKey;
    private final PlayRecordService playRecordService;
    private final MovieServiceInvoker movieServiceInvoker;
    public MoviePlayerServiceImpl(MoviePlayerMapper mapper, RedisService redisService, RedisKeyProperties redisKey,
                                  PlayRecordService playRecordService, MovieServiceInvoker movieServiceInvoker) {
        super(mapper);
        this.redisService = redisService;
        this.redisKey = redisKey;
        this.playRecordService = playRecordService;
        this.movieServiceInvoker = movieServiceInvoker;
    }

    @Override
    public Long save(@NotNull Token token, @NotNull UuidParam param) {
        String key = redisKey.key("us","qr",param.getUuid());
        QR qr = redisService.hash(key, QR.class);
        ErrorCode.QR_CODE_EXPIRED.assertNotNull(qr,key);
        ErrorCode.QR_TYPE_ERROR.assertTrue(qr.typePlayRecord(),"开本记录");
        PlayRecord record = playRecordService.select(qr.getId());
        ErrorCode.RECORD_NOT_EXIST.assertNotNull(record,qr.getId());
        MoviePlayer player = select(token.getUserId(),qr.getId());
        if(player == null){
            player = new MoviePlayer();
            player.setPlayRecordId(record.getId());
            player.setUserId(token.getUserId());
            player.setUsername(token.getUsername());
            insertSelective(player);
        }
        return player.getId();
    }

    @Override
    public MoviePlayer select(Long userId, Long playRecordId) {
        if(userId == null || playRecordId == null){
            return null;
        }
        return mapper.select(userId,playRecordId);
    }

    @Override
    public MoviePlayerVo mine(Token token, Long id) {
        MoviePlayer po = select(id);
        ErrorCode.RECORD_NOT_EXIST.assertNotNull(po,id);
        ErrorCode.PLAY_RECORD_ACCESS_DENIED.assertEqual(po.getUserId(),token.getUserId(),po.getPlayRecordId());
        return po2vo(po);
    }

    @NotNull
    @NotNullElement
    @Override
    public List<MoviePlayerVo> players(@NotNull Token token, @Nullable Long playRecordId) {
        if(playRecordId == null){
            return Collections.emptyList();
        }
        PlayRecord pr = playRecordService.select(playRecordId);
        ErrorCode.RECORD_NOT_EXIST.assertNotNull(pr,playRecordId);
        List<MoviePlayer> list = listByProperty("playRecordId",playRecordId);
        List<Long> userIds = list.stream().map(MoviePlayer::getUserId).collect(Collectors.toList());
        // 只有dm或者本次开本记录中的玩家才可以查询
        ErrorCode.PLAY_RECORD_ACCESS_DENIED.assertTrue(Objects.equals(token.getUserId(),pr.getDmId())
                || userIds.contains(token.getUserId()),playRecordId);
        return list.stream().map(this::po2vo).collect(Collectors.toList());
    }

    private MoviePlayerVo po2vo(MoviePlayer po){
        if(po == null){
            return null;
        }
        MoviePlayerVo vo = new MoviePlayerVo();
        BeanUtils.copyProperties(po,vo);
        return vo;
    }

    @Override
    public void clearRole(@NotNull Token token,@Nullable Long id) {
        validateAuthority(token, id);
        mapper.clearRecordRole(id);
    }

    @NotNull
    private PlayRecord validateAuthority(@NotNull Token token, @Nullable Long id) {
        token.validAnyAuthority(RoleEnum.DM);
        MoviePlayer player = select(id);
        ErrorCode.RECORD_NOT_EXIST.assertNotNull(player, id);
        PlayRecord pr = playRecordService.select(player.getPlayRecordId());
        ErrorCode.RECORD_NOT_EXIST.assertNotNull(pr,player.getPlayRecordId());
        ErrorCode.PLAY_RECORD_ACCESS_DENIED.assertEqual(token.getUserId(),pr.getDmId(),pr.getId());
        return pr;
    }

    @Transactional
    public void bindRole(@NotNull Token token,@Nullable Long id,Long playRoleId){
        PlayRecord pr = validateAuthority(token,id);
        PlayRoleInstVo playRoleInst = movieServiceInvoker.playRoleInst(pr.getMovieId(),pr.getMovieVersion(),playRoleId);
        ErrorCode.PLAY_ROLE_NOT_EXIST.assertNotNull(playRoleInst,playRoleId);
        mapper.clearRoleInfo(pr.getId(),playRoleInst.getPlayRoleId());
        mapper.updateRole(id, playRoleInst.getId(), playRoleInst.getPlayRoleId());
    }

    @Override
    public void removeUser(Token token, Long id) {
        validateAuthority(token,id);
        delete(id);
    }
}
